package POC.e2;

import java.util.*;

/**
 * @author sunshine
 * ll(1)算法实现
 */
public class LL1 {

    //请注意，这里存放为类变量不是好方法，但是因为这里是做实验，所以就这么简单存放免得传参了。
    /**
     * FIRST集
     * FIRST集对每一个符号来说都有一个，这里，我们采用kv方式来存放对应的FIRST集
     */
    private Map<String, Set<String>> firstSet;

    //FOLLOW集。原理同上
    private Map<String, Set<String>> followSet;

    //生成语句集
    Map<String, String[]> productionMap;

    //预测分析表M。sunshine小问题：为什么M表应该时Set<String, String>，而这里只用了Set<String>呢
    Set<String>[][] M;

    //开始符
    String start;

    //follow集已经访问过的
    List<String> close;

    public static void main(String[] args) {
        LL1 ll1 = new LL1();

        ll1.init();
    }

    /**
     * 初始化LL(1)
     */
    private void init() {
        firstSet = new HashMap<>();
        followSet = new HashMap<>();
        productionMap = new HashMap<>();

//        String[] grammar = {
//                "S->ABC",
//                "A->a|`",
//                "B->b",
//                "C->d"
//        };
        String[] grammars = {
                "E->TY",
                "Y->+TY|`",
                "T->FZ",
                "Z->*FZ|`",
                "F->(E)|i"
        };

        close = new ArrayList<>();

        start = grammars[0].split("->")[0];

        for (String s : grammars) {
            String[] split1 = s.split("->");
            String[] split2 = split1[1].split("\\|");

            productionMap.put(split1[0], split2);

            //切记不能一遍遍历出first集和follow集，因为他们有先后联系的。
//            findFirst(split1[0], split2);
//            findFollow(split2);
        }

        for (String left : productionMap.keySet()) {
            findFirst(left, productionMap.get(left), false);
            followSet.put(left, new HashSet<>());
        }

        for (String left : productionMap.keySet()) {
            findFollow(left);
        }

//        for (String left : productionMap.keySet()) {
//            System.out.println(left + "的first:" + Arrays.toString(firstSet.get(left).toArray()));
//        }
//
//        for (String left : productionMap.keySet()) {
//            System.out.println(left + "的follow:" + Arrays.toString(followSet.get(left).toArray()));
//        }

        initPredictiveAnalysisTable();

        ll1("i+i*i#");

//        //获得到终结符和非终结符表
//        Map<String, Integer> NMap = getNMap();
//        Map<String, Integer> TMap = getTMap();
//
//        for (int i = 1;i<TMap.keySet().toArray().length;i++) {
//            System.out.print(TMap.keySet().toArray()[i] + "\t\t\t\t\t");
//        }
//        System.out.println(TMap.keySet().toArray()[0]);
//        System.out.println("\n");
//
//        for (int i = 0;i<NMap.size();i++) {
//            for (int j = 0;j<TMap.size();j++) {
///*                for (String m : M[i][j]) {*/
//                try {
//                    System.out.print(((String)NMap.keySet().toArray()[i]).replace("Z", "T`").replace("Y", "E`") + "->" + ((String)(M[i][j].toArray()[0])).replace("Z", "T`").replace("Y", "E`") + " ");
//                }catch (Exception e) {
//                    System.out.print("空空空空");
//                }
//
///*                }*/
//                System.out.print("\t\t\t\t");
//            }
//            System.out.println();
//        }
    }

    /**
     * 找FIRST集
     * S->AB|C
     * @param left      S
     * @param rights    AB|C
     * @param isM       是否是预测分析表调用，预测分析表第一次进入不需要判断它是不是终结符
     * @return          first集
     */
    private Set<String> findFirst(String left, String[] rights, boolean isM) {
        //System.out.println("开始处理" + left + Arrays.toString(rights) + "-------------\n");
        //如果已经找过这个left的first，直接返回
        if (firstSet.containsKey(left)) return firstSet.get(left);

        Set<String> thisFirstSet = new HashSet<>();

        //对于终结符，它的first是它本身
        if (!productionMap.containsKey(left) && !isM) {
            thisFirstSet.add(left);
            firstSet.put(left, thisFirstSet);
            return thisFirstSet;
        }

        //遍历生成符号
        for (String right : rights) {
            //right就是S->AB|C中的AB
            for (int i = 0;i<right.length();i++) {
                //itemRight是右边分开后的一个，比如，S->AB|C,itemRight就是AB中的A
                String itemRight = String.valueOf(right.charAt(i));

                //如果左边不含这个符号，则说明它是一个终结符，我们可以将它记录
                if (!productionMap.containsKey(itemRight)) {
                    //System.out.println(left + "的生成遇到终结符" + itemRight);
                    thisFirstSet.add(itemRight);
                    break;
                }
                //如果左边含有这个符号，则说明它不是一个终结符，我们可以继续进行判断
                else {
                    //如果是E'这种情况，则向后移动一位
                    if (itemRight.equals("'")) {
                        itemRight+="'";
                        ++i;
                    }
                    //如果是S->AB这种形式，则找一下A的first集
                    if (productionMap.containsKey(itemRight)) {
                        Set<String> tempFirstSet = findFirst(itemRight, productionMap.get(itemRight), false);
                        //System.out.println(left + "的生成遇到非终结符终结符" + itemRight + "它的first集有：" + Arrays.toString(tempFirstSet.toArray()));
                        thisFirstSet.addAll(tempFirstSet);
                        break;
                    }
                    //用`表示空串
                    if (itemRight.equals("`")) {
                        //System.out.println(left + "的生成遇到空" + itemRight);
                        thisFirstSet.add("`");
                        break;
                    }
                }
            }
        }

        //所有的first集中加入这个first集
        firstSet.put(left, thisFirstSet);

        //System.out.println("结束处理" + left + "-------------\n");
        return thisFirstSet;
    }

    private void findFollow(String target) {
        //System.out.println("------\n开始找" + target);
        //开始字符...#
        if (target.equals(start)) followSet.get(target).add("#");

        for (String left : productionMap.keySet()) {
            for (String right : productionMap.get(left)) {
                if (right.contains(target)) {
                    //接下来split做法可能不太对，但是一般的还是能做的...
                    String[] leftRight = right.split(target);
                    //target在结尾的情况，αB
                    if (leftRight.length == 1) {
                        //如果左和当前字符相等，那么就不找了
                        if (!left.equals(target)) {
                            if (!close.contains(left)) findFollow(left);
                            //把follow(A)加入follow(B)
                            //System.out.println(target +"在结尾 " +  "要找" + left + "的follow集，它的follow集是" + Arrays.toString(followSet.get(left).toArray()));
                            followSet.get(target).addAll(followSet.get(left));
                        }

                    }
                    //target在中间和开头的情况，即A->αBβ
                    else {
                        //如果target下一位是非终结符
                        String next = String.valueOf(leftRight[1].charAt(0));
                        if (leftRight[1].length() > 1 && leftRight[1].charAt(1) == '\'') next+="'";
                        //是非终结符情况
                        if (productionMap.containsKey(next)) {
                            //要遍历next的right中的每一个
                            //System.out.println(next + "含有" + productionMap.get(next).length + "个生成式，他们是" + Arrays.toString(productionMap.get(next)));
                            for (String leftProduction : productionMap.get(next)) {
                                findFirst(leftProduction, new String[]{leftProduction}, true);
                                Set<String> nextFirstSet = firstSet.get(leftProduction);
                                //如果β->空
                                if (nextFirstSet.contains("`")) {
                                    //如果左和当前字符相等，那么就不找了
                                    if (!left.equals(target)) {
                                        if (!close.contains(left)) findFollow(left);
                                        //把follow(A)加入follow(B)
                                        //System.out.println(target +"下一位是非终结符 " +  next +"，要找它的first集，并且它的first集含有空串，" +  "要找" + left + "的follow集，它的follow集是" + Arrays.toString(followSet.get(left).toArray()));
                                        followSet.get(target).addAll(followSet.get(left));
                                    }
                                }
                                //不为空把first(β)/空 加入follow(B)中
                                else {
                                    //System.out.println(target + "中加入first " + next + Arrays.toString(firstSet.get(next).toArray()));
                                    followSet.get(target).addAll(firstSet.get(next));
                                    followSet.get(target).remove("`");
                                }
                            }
                        }
                        //不是非终结符
                        else {
                            //System.out.println("终结符 " + target + " " + next);
                            followSet.get(target).add(next);
                        }
                    }
                }
            }
        }

        close.add(target);
        //System.out.println("已经找到 " + target + "\n-----------");
    }

//    /**
//     *
//     * @param left
//     * @param rights
//     */
//    private void findFollow(String left, String[] rights) {
//        //遍历所有生成式
//        for (String right : rights) {
//            //遍历所有生成式的每一个字符
//            for (int i = 0;i<right.length();i++) {
//                String thisChar = String.valueOf(right.charAt(i));
//                //如果它是一个非终结符，那么就考虑下一个
//                int next = i+1;
//
//                //如果还有下一个字符
//                if (next < right.length() - 1) {
//                    String nextChar = String.valueOf(right.charAt(next));
//                    if (productionMap.containsKey(thisChar)) {
//                        //如果下一个是一个E ' 符号，那么跳过它
//                        if ('\'' == right.charAt(next)){
//                            i++;
//                            next++;
//                            thisChar += "'";
//                        }
//                        //如果下一个字符的下一个还是'的话
//                        if ('\'' == right.charAt(next+1)) {
//                            nextChar += "'";
//                        }
//                        //如果下一个字符是非终结符
//                        if (productionMap.containsKey(nextChar)) {
////                            Set<String> thisSet = followSet.getOrDefault(thisChar, new HashSet<>());
////                            thisSet.addAll(firstSet.get(nextChar));
////                            thisSet.remove("`");
////                            followSet.put(thisChar, thisSet);
//                            //下一个非终结符的产生式们
//                            String[] nextRights = productionMap.get(nextChar);
//                            for (String nextRight : nextRights) {
//                                //如果β->`，则把follow(A)加入follow(B)中
//                                if ("`".equals(nextRight)) {
//                                    findFollow();
//                                }
//                            }
//                        }
//                    }
//                }
//
//            }
//        }
//    }

//    /**
//     * 寻找follow集，其实这个和寻找first集有着不可分的关系。
//     * 我们发现，一个字符的follow集就是紧接着它的终结符或者它的下一个非终结符的first集
//     * 如果它是最后一个字符，则把#加入follow集
//     * eg. S->A|BC
//     * @param rights    A|BC*/
//
//    private void findFollow(String left, String[] rights) {
//        //右边所有
//        for (String right : rights) {
//            for (int i = 0;i<right.length();i++) {
//                //如果是空串的话，就结束内循环
//                if (right.charAt(i) == '\'') break;
//                //targetChar是要加入它的follow集。它就相当于...Aab...中的A
//                String targetChar;
//                //双重for循环用于寻找出每一个非终结符的follow集。因为一个follow集就需要从第一个非终结符开始遍历
//                for (int j = i;j<right.length();j++) {
//                    String itemRight = String.valueOf(right.charAt(j));
//                    //如果是非终结符，就要进行判断
//                    if (productionMap.containsKey(itemRight)) {
//                        targetChar = itemRight;
//                        //如果下一位是终结符，那么把终结符加入follow集
//                        if (j+1<right.length()) {
//                            //如果下一位是终结符，则直接加入
//                            String nextString = String.valueOf(right.charAt(j + 1));
//
//                            //-----
//                            //明白吧
//                            if (nextString.equals("'")) {
//                                targetChar+="'";
//                                j++;
//                            }
//                            if (j+1<right.length()) {
//                                nextString = String.valueOf(right.charAt(j + 1));
//                                j++;
//                            }
//                            if (j+1<right.length() && String.valueOf(right.charAt(j + 1)).equals("'")) {
//                                nextString+="'";
//                            }
//                            //-----
//
//                            if (!productionMap.containsKey(nextString)) {
//                                //需要判断当前字符follow集是否存在，因为可能不止一遍需要添加一个非终结符的follow集
//                                Set<String> itemFollowSet;
//                                if (null == followSet.get(targetChar)) itemFollowSet = new HashSet<>();
//                                else itemFollowSet = followSet.get(targetChar);
//
//                                itemFollowSet.add(nextString);
//                                followSet.put(targetChar, itemFollowSet);
//                            }
//
//                            //如果下一位是非终结符，则要把它的first集加入
//                            else {
//                                Set<String> itemFirstSet = findFirst(nextString, productionMap.get(nextString), false);
//
//                                //需要判断当前字符follow集是否存在，因为可能不止一遍需要添加一个非终结符的follow集
//                                Set<String> itemFollowSet;
//                                if (null == followSet.get(targetChar)) itemFollowSet = new HashSet<>();
//                                else itemFollowSet = followSet.get(targetChar);
//
//                                itemFollowSet.addAll(itemFirstSet);
//
//                                followSet.put(targetChar, itemFollowSet);
//                            }
//                            break;//结束这个非终结符的遍历
//                        }
//
//                        //如果是最后一位，则要把#加入
//                        else {
//                            Set<String> itemFollowSet;
//                            if (null == followSet.get(targetChar)) itemFollowSet = new HashSet<>();
//                            else itemFollowSet = followSet.get(targetChar);
//
//                            itemFollowSet.add("#");
//
//                            followSet.put(targetChar, itemFollowSet);
//                        }
//                    }
//                }
//            }
//        }
//    }

    /**
     * 构造预测分析表
     * 1. 对于每一产生式 A→α，执行2.和3.；
     * 2. 对于 FIRST(α)中的每一终结符a, 将 A→α 填入 M[A,a]；
     * 3. 如果ε属于 FIRST(α)，则对FOLLOW(A)中的每个符号b，将A→α填入M[A,b]；
     *    若ε属于 FIRST(α)，且#在FOLLOW(A)，则将A→α填入M[A,#]；
     * 4. 将所有无定义的 M[A,b] 标上错误标志
     */
    private void initPredictiveAnalysisTable() {
        //获得到终结符和非终结符表
        Map<String, Integer> NMap = getNMap();
        Map<String, Integer> TMap = getTMap();
        //初始化M矩阵
        //遵循，[非终结符][终结符]样式
        M = new Set[NMap.size()][TMap.size()];
        for (int i = 0; i < NMap.size(); i++) {
            for (int j = 0; j < TMap.size(); j++) {
                //M[A,a]集合。至于为什么不需要把A存进去，参考定义时小问题
                M[i][j] = new HashSet<>();
            }
        }

        for (String left : productionMap.keySet()) {
            for (String right : productionMap.get(left)) {
                findFirst(right, new String[]{right}, true);
                //如果空在first(right)里，那么就把follow(left)加入
                if (firstSet.get(right).contains("`")) {
                    for (String b : followSet.get(left)) {
                        //System.out.println("空在first(" + right + ")中，把" + left + "->" + right + "加到M[" + left + "," + b + "]中");
                        M[NMap.get(left)][TMap.get(b)].add(right);
                    }
                } else {
                    //遍历产生式的first集，加入
                    for (String itemChar : firstSet.get(right)) {
                        //System.out.println("空不在first(" + right + ")中，把" + left + "->" + right + "加到M[" + left + "," + itemChar + "]中");
                        M[NMap.get(left)][TMap.get(itemChar)].add(right);
                    }
                }
            }

//                //如果生成为空，需要找follow(A)
//                if ("`".equals(right)) {
//                    Set<String> itemFollowSet = followSet.get(left);
//                    for (String item : itemFollowSet) {
//                        //如果是空串产生式A→α，求FOLLOW(A)，对于FOLLOW(A)中的每一个终结符号b，M(A,B)=A→ε
//                        M[NMap.get(left)][TMap.get(item)].add(right);
//                    }
//                }
//                //如果不为空，那么需要找first(阿尔法)
//                else {
//                    //产生式第一个字符是终结符的话那么first(α)必然只有它一个
//                    if (TMap.containsKey(String.valueOf(right.charAt(0)))) {
//                        M[NMap.get(left)][TMap.get(String.valueOf(right.charAt(0)))].add(right);
//                    }
//                    //如果不是的话，我们需要找它的first(α)
//                    else {
//                        findFirst(right, new String[]{right}, true);
//                        for (String item : firstSet.get(right)) {
//                            if (!"`".equals(item)) {
//                                M[NMap.get(left)][TMap.get(item)].add(right);
//                            }
//                        }
//                    }
//                }
        }

//        //遍历所有非终结符
//        for (String left : NMap.keySet()) {
//            //得到所有生成字符
//            for (String alpha : productionMap.get(left)) {
//                //得到阿尔法，就是右端的一个生成式的first集
//                Set<String> itemFirstSet = findFirst(alpha, productionMap.get(alpha));
//                //遍历阿尔法的first集，如果它是终结符，就将 A→α 填入 M[A,a]
//                for (String itemChar : itemFirstSet) {
//                    //这个判断好像没有用，first集都是终结符吧。除了空串
//                    //if (TMap.containsKey(itemChar)) {
//                        //对于空串，它必定在终结符集中，我们应该排除掉稍后处理
//                        if (!itemChar.equals("`")) {
//                            M[NMap.get(left)][TMap.get(itemChar)].add(alpha);
//                        }
//                        //如果ε属于 FIRST(α)，则对FOLLOW(A)中的每个符号b，将A→α填入M[A,b]；
//                        else {
//                            //A的follow集
//                            Set<String> itemFollowSet = followSet.get(left);
//                            for (String itemFollow : itemFollowSet) {
//                                //将A→α填入M[A,b]；
//                                M[NMap.get(left)][TMap.get(itemFollow)].add(alpha);
//                            }
//                        }
//                    //}
//                }
//            }
//        }
    }

    /**
     * 获取非终结符表
     * key:字符 values:位置
     * @return  非终结符集合
     */
    private Map<String, Integer> getNMap() {
        Map<String, Integer> NMap = new HashMap<>();
        int i = 0;
        for (String left : productionMap.keySet()) {
            NMap.put(left, i++);
        }
        return NMap;
    }

    /**
     * 获取终结符表
     * @return  终结符表
     */
    private Map<String, Integer> getTMap() {
        Set<String> TSet = new HashSet<>();
        for (String left : productionMap.keySet()) {
            String[] rights = productionMap.get(left);
            for (String right : rights) {
                for (int i = 0;i<right.length();i++) {
                    TSet.add(String.valueOf(right.charAt(i)));
                }
            }
        }
        //把非终结符去掉
        TSet.removeAll(productionMap.keySet());
        //移除掉空串
        TSet.remove("`");

        Map<String, Integer> TMap = new HashMap<>();
        int i = 0;
        for (String left : TSet) {
            TMap.put(left, i++);
        }
        //#需要自己添加
        TMap.put("#", i);
        return TMap;
    }

    private void ll1(String input){
        char[] inputChar = input.toCharArray();
        int i = 0;
        Stack<Character> stack = new Stack<>();
        stack.push('#');
        stack.push(start.charAt(0));

        while (i<inputChar.length) {
            if ('#' == stack.peek()) break;
            //把之前的出栈
            //System.out.println("栈：" + Arrays.toString(stack.toArray()));
            Character top = stack.pop();
            String temp = (String) M[getNMap().get(String.valueOf(top))][getTMap().get(String.valueOf(inputChar[i]))].toArray()[0];
            //System.out.println("输出：" + top + "->" + M[getNMap().get(String.valueOf(top))][getTMap().get(String.valueOf(inputChar[i]))].toArray()[0]);
            //得到M表中的生成式，入栈
            for (int c = temp.length() - 1;c>=0;c--) {
                //空串不入栈
                if (temp.charAt(c) != '`') stack.push(temp.charAt(c));
            }

            //如果栈顶是当前要匹配的字符，就出栈
            if (inputChar[i] == stack.peek()) {
                stack.pop();
                i++;
            }
        }

        if (i == inputChar.length) {
            System.out.println("匹配成功");
        }

    }

//    /**
//     * M表的类
//     */
//    static class M {
//
//        //非终结符，也就是左边的那部分
//        private String N;
//
//        //终结符，就是我们平时输入的字符
//        private String T;
//
//        private Map<Integer, Set<Map<String, String>>>
//
//        M(String N, String T) {
//            this.N = N;
//            this.T = T;
//
//        }
//    }

//    private Set<String> findFIRST(String X) {
//        Set<String> itemFIRST = new HashSet<>();
//
//        //（1）若X包含于VT，则FIRST(X)={X}
//        //这里用contains一定会找出包含的吗？
//        if (cfg.getVTSymbolSet().contains(X)) {
//            itemFIRST.add(X);
//            return itemFIRST;
//        }
//
//        if (cfg.getVNSymbolSet().contains(X)) {
//            //（2）若X包含于VN，且有产生式X->a...，则把a加入到FIRST(X)中；若X->空也是一条产生式，则把空也加到FIRST(X)中
//            for (List<String> production : cfg.getProductionMap().get(X)) {
//                //cfg.getProductionMap().get(VNItem):产生式列表，就是A|B中，由|分隔开的两部分
//                //production：就是A和B
//
//                //若X包含于VN，且有产生式X->a...
//                //sunshine小问题：为什么这里不用简单的contains呢？
//                //if (inputStringList.contains(production.get(0)))
//                String firstChar = production.get(0);
//                for (String s : inputStringList) {
//                    if (s.equals(firstChar)) {
//                        itemFIRST.add(s);
//                    }
//                }//end for inner
//            }//end for out
//        }
//
//        for (List<String> production : cfg.getProductionMap().get(X)) {
//            //cfg.getProductionMap().get(VNItem):产生式列表，就是A|B中，由|分隔开的两部分
//            //production：就是A和B
//            //（3.1）若X->Y是一个产生式且Y包含于VN，则把FIRST(Y)中的所有非空元素都加到FIRST(X)中
//            if (cfg.getVNSymbolSet().contains(production.get(0))) {
//                Set<String> FIRST_Y;
//                //先判断FIRST集中是否有这个符号的FIRST集
//                if (firstSet.containsKey(production.get(0))) {
//                    FIRST_Y = firstSet.get(production.get(0));
//                } else {
//                    FIRST_Y = findFIRST(production.get(0));
//                }
//                //用 ` 表示空串，因为这里要非空元素
//                FIRST_Y.remove("`");
//                itemFIRST.addAll(FIRST_Y);
//            }
//
//            //(3.2)若X->Y1Y2Y3...YK是产生式，Y1,...Yi-1都是非终结符
//            int count;
//            for (count = 0; count<production.size(); count++) {
//                String word = production.get(count);
//                //是非终结符，如果不是则结束遍历了
//                if (!cfg.getVNSymbolSet().contains(word)) break;
//            }
//
//            //(3.2)而且，对于任何j，1<=j<=i-1，FIRST(YJ)都含有空串
//            int count1;
//            for (count1=0;count1<count;count1++) {
//                Set<String> FIRST_Y;
//                //先判断FIRST集中是否有这个符号的FIRST集
//                if (firstSet.containsKey(production.get(count1))) {
//                    FIRST_Y = firstSet.get(production.get(count1));
//                } else {
//                    FIRST_Y = findFIRST(production.get(count1));
//                }
//                //判断是否有空串，没有则结束遍历了
//                if (!FIRST_Y.contains("`")) break;
//            }
//
//            //(3.2)则把FIRST(Yi)中的所有非空元素都加到FIRST(X)中
//            if (count1 == count) {
//                //特别的，若所有的FIRST(Yj)均含有空，j=1,2,...,k,则把空家到FIRST(X)中
//                if (count == production.size()) {
//                    itemFIRST.add("`");
//                } else {
//                    Set<String> FIRST_Y;
//                    //先判断FIRST集中是否有这个符号的FIRST集
//                    if (firstSet.containsKey(production.get(count1))) {
//                        FIRST_Y = firstSet.get(production.get(count1));
//                    } else {
//                        FIRST_Y = findFIRST(production.get(count1));
//                    }
//                    //用 ` 表示空串，因为这里要非空元素
//                    FIRST_Y.remove("`");
//                    itemFIRST.addAll(FIRST_Y);
//                }
//            }
//        }
//        return itemFIRST;
//    }

//    /**
//     * 上下文无关文法类
//     */
//    static class CFG {
//        //private在这里用处不大，因为静态内部类对父类是可见的
//        //开始符号。它应该在非终结符号中存在
//        private String startSymbol;
//
//        //终结符号
//        private Set<String> VTSymbolSet;
//
//        //非终结符号
//        private Set<String> VNSymbolSet;
//
//        //产生式表
//        private Map<String, List<List<String>>> productionMap;
//
//        public CFG(String startSymbol, Set<String> VTSymbolSet, Set<String> VNSymbolSet, Map<String, List<List<String>>> productionMap) {
//            this.startSymbol = startSymbol;
//            this.VTSymbolSet = VTSymbolSet;
//            this.VNSymbolSet = VNSymbolSet;
//            this.productionMap = productionMap;
//        }
//
//        public String getStartSymbol() {
//            return startSymbol;
//        }
//
//        public void setStartSymbol(String startSymbol) {
//            this.startSymbol = startSymbol;
//        }
//
//        public Set<String> getVTSymbolSet() {
//            return VTSymbolSet;
//        }
//
//        public void setVTSymbolSet(Set<String> VTSymbolSet) {
//            this.VTSymbolSet = VTSymbolSet;
//        }
//
//        public Set<String> getVNSymbolSet() {
//            return VNSymbolSet;
//        }
//
//        public void setVNSymbolSet(Set<String> VNSymbolSet) {
//            this.VNSymbolSet = VNSymbolSet;
//        }
//
//        public Map<String, List<List<String>>> getProductionMap() {
//            return productionMap;
//        }
//
//        public void setProductionMap(Map<String, List<List<String>>> productionMap) {
//            this.productionMap = productionMap;
//        }
//    }

//    /**
//     * 产生式类
//     */
//    static class Production {
//        //被生成符号S->
//        private String begin;
//
//        //生成符号列表，有顺序 S->AB
//        private List<List<String>> toList;
//
//        public Production(String begin, List<List<String>> toList) {
//            this.begin = begin;
//            this.toList = toList;
//        }
//
//        public String getBegin() {
//            return begin;
//        }
//
//        public void setBegin(String begin) {
//            this.begin = begin;
//        }
//
//        public List<List<String>> getToList() {
//            return toList;
//        }
//
//        public void setToList(List<List<String>> toList) {
//            this.toList = toList;
//        }
//    }

//    /**
//     * 符号类，用于存储符号名字和对应类型（终结符、非终结符）
//     */
//    static class Symbol {
//
//        //非终结符
//        final static int VN = 0;
//
//        //终结符
//        final static int VT = 1;
//
//        //符号名字
//        private String name;
//
//        //符号类型
//        private int type;
//
//        Symbol(String name, int type) {
//            this.name = name;
//            this.type = type;
//        }
//
//        public String getName() {
//            return name;
//        }
//
//        public void setName(String name) {
//            this.name = name;
//        }
//
//        public int getType() {
//            return type;
//        }
//
//        public void setType(int type) {
//            this.type = type;
//        }
//
//        @Override
//        public boolean equals(Object o) {
//            if (this == o) return true;
//            if (o == null || getClass() != o.getClass()) return false;
//            Symbol symbol = (Symbol) o;
//            return type == symbol.type &&
//                    name.equals(symbol.name);
//        }
//
//        @Override
//        public int hashCode() {
//            return Objects.hash(name, type);
//        }
//    }
}
